/* Packages */
package edu.gatech.team404.tool;

import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.Collection;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;

/**
 * Provide a logging system for debugging purposes.
 * @author Michael Dandy
 * @version 1.0
 */
public class Log {
	/** Path to the output file */
	private static final String PATH = "/log/";
	
	/** Entry in the log */
	private static final Collection<Entry> log = new LinkedList<Entry>();

	/**
	 * Clear the log.
	 */
	public static void clear() {
		log.clear();
	}
	
	/**
	 * Generate an information logging.
	 * @param tag the tag of the log
	 * @param value	the value of the log
	 */
	public static void info(String tag, String value) {
		final Entry e = new Entry(Type.INFO, tag, value);
		log.add(e);
		System.out.println (e.toString());
	}
	
	/**
	 * Generate an error logging. If it is possible, use the exception version
	 * instead as it will give more information about the error.
	 * @param tag the tag of the log
	 * @param value	the value of the log
	 */
	public static void error(String tag, String value) {
		final Entry e = new Entry(Type.ERROR, tag, value);
		log.add(e);
		System.err.println(e.toString());
	}
	
	/**
	 * Generate an error logging
	 * @param tag the tag of the log
	 * @param e the exception that occurs
	 */
	public static void error(String tag, Exception e) {
		final StringWriter sw = new StringWriter();
		final PrintWriter pw = new PrintWriter(sw);
		e.printStackTrace(pw);
		
		final Entry entry = new Entry(Type.ERROR, tag, e.getMessage() 
							+ "\n" + sw.toString());
		log.add(entry);
		System.err.println (entry.toString());
		
		try {
			sw.close();
			pw.close();
		} 
		catch (IOException ex) {
			System.err.println("Error: " + ex.getMessage());
		}
		
	}
	
	/**
	 * Generate a warning logging.
	 * @param tag the tag of the log
	 * @param value	the value of the log
	 */
	public static void warning(String tag, String value) {
		final Entry e = new Entry(Type.WARNING, tag, value);
		log.add(e);
		System.out.println (e.toString());
	}
	
	/**
	 * Generate a verbose logging.
	 * @param tag the tag of the log
	 * @param value	the value of the log
	 */
	public static void verbose(String tag, String value) {
		final Entry e = new Entry(Type.VERBOSE, tag, value);
		log.add(e);
		System.out.println (e.toString());
	}
	
	/**
	 * Print the information logging to the console.
	 */
	public static void printInfo() {
		for (Entry e : log) {
			if (e.getType() == Type.INFO) {
				System.out.println (e.toString());
			}
		}
	}
	
	/**
	 * Print the error logging to the console.
	 */
	public static void printError() {
		for (Entry e : log) {
			if (e.getType() == Type.ERROR) {
				System.out.println (e.toString());
			}
		}
	}
	
	/**
	 * Print the warning logging to the console.
	 */
	public static void printWarning() {
		for (Entry e : log) {
			if (e.getType() == Type.WARNING) {
				System.out.println (e.toString());
			}
		}
	}
	
	/**
	 * Print the verbose logging to the console. Verbose will output everything, so
	 * do not need to combine this with other type of logging.
	 */
	public static void printVerbose() {
		for (Entry e : log) {
			System.out.println (e.toString());
		}
	}
	
	/**
	 * Output to a file the information logging.
	 */
	public static void outputInfo() {
		String content = "";
		for (Entry e : log) {
			if (e.getType() == Type.INFO) {
				content += e.toString() + "\n";
			}
		}
		writeToFile(content);
	}
	
	/**
	 * Output to a file the error logging.
	 */
	public static void outputError() {
		String content = "";
		for (Entry e : log) {
			if (e.getType() == Type.ERROR) {
				content += e.toString() + "\n";
			}
		}
		writeToFile(content);
	}
	
	/**
	 * Output to a file the warning logging.
	 */
	public static void outputWarning() {
		String content = "";
		for (Entry e : log) {
			if (e.getType() == Type.WARNING) {
				content += e.toString() + "\n";
			}
		}
		writeToFile(content);
	}
	
	/**
	 * Output to a file the verbose logging. Verbose will output everything, so
	 * do not need to combine this with other type of logging.
	 */
	public static void outputVerbose() {
		String content = "";
		for (Entry e : log) {
			content += e.toString() + "\n";
		}
		writeToFile(content);
	}
	
	/**
	 * Write to a file.
	 * @param content the content to be written
	 */
	private static void writeToFile(String content) {
		try {
			final Calendar cal = Calendar.getInstance();
		    final SimpleDateFormat sdf = new SimpleDateFormat("MMddyyyy");
			final String fileName = sdf.format(cal.getTime()) + ".txt";
			
			final File logFile= new File (PATH + fileName);
			final FileWriter fstream = new FileWriter(logFile);
			final BufferedWriter out = new BufferedWriter(fstream);
			out.write(content);

			out.close();
			fstream.close();
		}
		catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
	}
	
	/**
	 * Append log to the existing file.
	 * @param content the content to be appended
	 */
	private static void appendToFile(String content) {
		try {
			final Calendar cal = Calendar.getInstance();
		    final SimpleDateFormat sdf = new SimpleDateFormat("MMddyyyy");
			final String fileName = sdf.format(cal.getTime()) + ".txt";

			final File logFile= new File (PATH + fileName);
			final FileWriter fstream = new FileWriter(logFile, true);
			final BufferedWriter out = new BufferedWriter(fstream);
			out.write(content);
			
			out.close();
			fstream.close();
		}
		catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
	}
	
	/**
	 * A class that represents an entry within a Log.
	 * @author Michael Dandy
	 * @version 1.0
	 */
	private static class Entry {
		/** The date of the entry */
		private final Date timeStamp;
		
		/** The type of the entry */
		private final Type type;
		
		/** The tag of the entry */
		private final String tag;
		
		/** The value of the entry */
		private final String value;
		
		/**
		 * Constructor
		 * @param type the type of an entry
		 * @param tag the tag of an entry
		 * @param value the value of an entry
		 */
		private Entry (Type type, String tag, String value) {
			final Calendar cal = Calendar.getInstance();
			this.timeStamp = cal.getTime();
				
			this.type = type;
			this.tag = tag;
			this.value = value;
		}
		
		/**
		 * Get the type of the entry
		 * @return the type of the entry
		 */
		public Type getType() {
			return type;
		}
		
		/**
		 * Print the entry
		 * @return the entry
		 */
		public String toString() {
			return timeStamp.toString() + ": " + type.toString() 
					+ ": " + tag + ": " + value;
		}
	}
	
	/**
	 * Enumeration of Log type
	 * @author Michael Dandy
	 * @version 1.0
	 */
	private enum Type {
		INFO {
		    public String toString() {
		        return "I";
		    }
		},
		
		ERROR {
		    public String toString() {
		        return "E";
		    }
		},
		
		WARNING {
		    public String toString() {
		        return "W";
		    }
		},
		
		VERBOSE {
		    public String toString() {
		        return "V";
		    }
		}
	}
}
